package com.pucong.gitee.database.backup.utils;

import com.baomidou.mybatisplus.core.toolkit.IOUtils;
import com.pucong.gitee.database.backup.common.entity.service.vo.ServiceFileTreeVo;
import com.pucong.gitee.database.backup.common.entity.sys.SysFile;
import com.pucong.gitee.database.backup.common.selfenum.CommonBoolenEnum;
import com.pucong.gitee.database.backup.configuration.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

/**
 * 这是FTP上传下载的工具类
 *
 * @author pucong
 * @date 2017/11/21
 */
@Slf4j
public class FtpUtils {

    private static final int DEFAULT_BUFFER_SIZE = 4096;

    /**
     * 上传文件
     *
     * @param input      输入流
     * @param outputPath 保存路径
     * @param fileName   文件名（包含格式后缀）
     * @throws IOException
     */
    public static void upload(FTPClient ftpClient, InputStream input, String outputPath, String fileName) {
        try {
            //判断路径是否存在，不存在则新建
            checkFTPPath(ftpClient, outputPath);
            //保存文件到路径
            ftpClient.storeFile(fileName, input);
            log.info("您的文件格式" + fileName + "，上传成功。");
        } catch (IOException e) {
            log.error("FTP上传文件时出错", e);
            throw new BusinessException("FTP上传文件时出错");
        } finally {
            //关闭连接
            IOUtils.closeQuietly(input);
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                log.error("FTP上传文件时关闭FTP连接出错", e);
                throw new BusinessException("FTP上传文件时关闭FTP连接出错");
            }
        }
    }

    /**
     * 删除文件
     *
     * @param path
     * @return
     * @throws IOException
     */
    public static Boolean deleteFile(FTPClient ftpClient, String path) {
        Boolean flag = false;
        try {
            //删除
            if (ftpClient != null) {
                flag = ftpClient.deleteFile(path);
                log.info("文件格式" + path + "，删除成功。");
            }
        } catch (IOException e) {
            log.error("FTP删除文件时出错", e);
            throw new BusinessException("FTP删除文件时出错");
        } finally {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                log.error("上传文件时关闭FTP连接出错", e);
                throw new BusinessException("上传文件时关闭FTP连接出错");
            }
        }
        return flag;
    }

    /**
     * 删除目录
     *
     * @param path
     * @return
     * @throws IOException
     */
    public static Boolean deleteDirectory(FTPClient ftpClient, String path) {
        Boolean flag = false;
        try {
            //删除
            if (ftpClient != null) {
                flag = ftpClient.removeDirectory(path);
                log.info("目录" + path + "，删除成功。");
            }
        } catch (IOException e) {
            log.error("FTP删除目录时出错", e);
            throw new BusinessException("FTP删除目录时出错");
        } finally {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                log.error("关闭FTP连接出错", e);
                throw new BusinessException("关闭FTP连接出错");
            }
        }
        return flag;
    }

    /**
     * 批量删除文件
     *
     * @param sysAttachmentList
     * @return
     * @throws IOException
     */
    public static Boolean deleteListFile(FTPClient ftpClient, List<SysFile> sysAttachmentList) throws IOException {
        Boolean flag = false;
        try {
            //删除
            if (ftpClient != null) {
                for (SysFile sysAttachment : sysAttachmentList) {
                    flag = ftpClient.deleteFile(sysAttachment.getPath());
                    log.info("文件" + sysAttachment.getPath() + "，删除成功。");
                }
            }
        } finally {
            try {
                ftpClient.disconnect();
            } catch (IOException e) {
                log.error("上传文件时关闭FTP连接出错", e);
                throw e;
            }
        }
        return flag;
    }

    /**
     * 校验路径是否存在，不存在则新建
     *
     * @param ftpClient
     * @param outputPath
     * @throws IOException
     */
    private static void checkFTPPath(FTPClient ftpClient, String outputPath) throws IOException {
        //判断路径是否存在，不存在则新建
        if (outputPath != null) {
            String[] paths = outputPath.split("/");
            StringBuffer tempPath = new StringBuffer();
            for (String path : paths) {
                if ((path != null) && (!"".equals(path.trim()))) {
                    String temp = tempPath.append("/").append(path.trim()).toString();
                    if ((!ftpClient.changeWorkingDirectory(temp)) && (ftpClient.makeDirectory(temp))) {
                        ftpClient.changeWorkingDirectory(temp);
                    }
                }
            }
        }
    }

    /**
     * 复制文件
     *
     * @param inputStream 输入流对象
     * @param toPath
     * @param toFileName
     * @return
     */
    public static Boolean copyDirectory(FTPClient ftpClient, InputStream inputStream, String toPath, String toFileName) {
        boolean copyFalg = false;
        try {
            //判断路径是否存在，不存在则新建
            checkFTPPath(ftpClient, toPath);
            if (inputStream != null) {
                copyFalg = ftpClient.storeFile(toPath + "/" + toFileName, inputStream);
                // 关闭文件流
                inputStream.close();
                if (!copyFalg) {
                    return copyFalg;
                }
            }
        } catch (IOException e) {
            log.error("复制文件失败 ", e);
            throw new BusinessException("复制文件失败");
        }
        return copyFalg;
    }

    /**
     * 从ftp上下载
     *
     * @param output     输出流
     * @param remoteFile 获取路径
     * @throws NumberFormatException
     * @throws SocketException
     * @throws IOException
     */
    public static void download(FTPClient ftpClient, OutputStream output, String remoteFile) throws NumberFormatException, SocketException, IOException {
        try {
            ftpClient.enterLocalPassiveMode();
            ftpClient.retrieveFile(remoteFile, output);
            output.flush();
        } catch (Exception e) {
            log.error("下载文件出错：" + e.getMessage());
        } finally {
            IOUtils.closeQuietly(output);
        }
    }

    /**
     * 根据名称获取文件，以字节数组返回
     *
     * @param ftpPath  FTP服务器文件相对路径，例如：/test/123
     * @param fileName 文件名，例如：12312321.123123
     * @return byte[] 字节数组对象
     */
    public static byte[] getFileBytesByName(FTPClient ftpClient, String ftpPath, String fileName) {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        try {
            // 判断是否存在该目录
            if (!ftpClient.changeWorkingDirectory(ftpPath)) {
                log.error(ftpPath + "该目录不存在");
                throw new BusinessException("该目录不存在");
            }
            ftpClient.enterLocalPassiveMode();  // 设置被动模式，开通一个端口来传输数据
            String[] fileNameList = ftpClient.listNames();
            // 判断该目录下是否有文件
            if (fileNameList == null || fileNameList.length == 0) {
                log.error(ftpPath + "该目录下没有文件");
                throw new BusinessException("该目录下没有文件");
            }
            for (String ftpFileName : fileNameList) {
                if (ftpFileName.equals(fileName)) {
                    try (InputStream is = ftpClient.retrieveFileStream(ftpFileName)) {
                        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
                        int len = -1;
                        while ((len = is.read(buffer, 0, DEFAULT_BUFFER_SIZE)) != -1) {
                            byteStream.write(buffer, 0, len);
                        }
                    } catch (IOException e) {
                        log.error("获取文件流失败", e);
                        throw new BusinessException("获取文件流失败");
                    }
                    break;
                }
            }
        } catch (IOException e) {
            log.error("获取文件流失败", e);
            throw new BusinessException("获取文件流失败");
        }
        return byteStream.toByteArray();
    }

    /**
     * 根据名称获取文件，以输入流返回
     *
     * @param ftpPath  FTP服务器上文件相对路径，例如：/test/123
     * @param fileName 文件名，例如：test.txt
     * @return InputStream 输入流对象
     */
    public static InputStream getInputStreamByName(FTPClient ftpClient, String ftpPath, String fileName) {
        InputStream input = null;
        try {
            // 判断是否存在该目录
            if (!ftpClient.changeWorkingDirectory(ftpPath)) {
                log.error(ftpPath + "该目录不存在");
                throw new BusinessException("该目录不存在");
            }
            ftpClient.enterLocalPassiveMode();  // 设置被动模式，开通一个端口来传输数据
            String[] fs = ftpClient.listNames();
            // 判断该目录下是否有文件
            if (fs == null || fs.length == 0) {
                log.error(ftpPath + "该目录下没有文件");
                throw new BusinessException("该目录下没有文件");
            }
            for (String ftpName : fs) {
                if (ftpName.equals(fileName)) {
                    input = ftpClient.retrieveFileStream(ftpName);
                    break;
                }
            }
        } catch (IOException e) {
            log.error("获取文件失败", e);
            throw new BusinessException("获取文件失败");
        }
        return input;
    }

    /**
     * 重命名、移动FTP文件
     *
     * @param remote 远程文件路径+名称
     * @param name   新远程文件路径+名称
     */
    public static void renameAndMove(FTPClient ftpClient, String remote, String name) {
        try {
            FTPFile[] files = ftpClient.listFiles(remote);
            if (files.length == 1) {
                boolean status = ftpClient.rename(remote, name);
                if (!status) {
                    log.error("重命名文件失败");
                    throw new BusinessException("重命名文件失败");
                }
            } else {
                log.error("重命名文件失败，远程文件不存在");
                throw new BusinessException("重命名文件失败，远程文件不存在");
            }
        } catch (IOException e) {
            log.error("重命名文件失败", e);
            throw new BusinessException("重命名文件失败");
        }
    }

    /**
     * 获取目录下面的文件信息
     *
     * @param path 需要遍历的目录，必须以"/"开始和结束
     * @throws IOException
     */
    public static List<ServiceFileTreeVo> getFileInfoList(FTPClient ftpClient, String path) throws IOException {
        List<ServiceFileTreeVo> serviceFileTreeVoArrayList = new ArrayList<>();
        if (path.startsWith("/") && path.endsWith("/")) {
            //更换目录到当前目录
            ftpClient.changeWorkingDirectory(path);
            FTPFile[] files = ftpClient.listFiles();

            for (FTPFile file : files) {
                ServiceFileTreeVo fileAttribute = new ServiceFileTreeVo();
                String filename = file.getName();
                filename = filename.toLowerCase();
                fileAttribute.setFileTrue(file.isFile());
                fileAttribute.setFileSize(file.getSize());
                if (file.isFile()) {
                    // 获取文件的后缀名
                    if(filename.lastIndexOf(".") >= 0){
                        String suffixName = filename.substring(filename.lastIndexOf(".") + 1);
                        fileAttribute.setFileSuffix(suffixName);
                    }
                }
                fileAttribute.setFileName(filename);
                fileAttribute.setLastUpdateTime(file.getTimestamp().getTime());
                fileAttribute.setFilePath(path + file.getName());
                serviceFileTreeVoArrayList.add(fileAttribute);
            }
        }
        return serviceFileTreeVoArrayList;
    }
}
